package com.theaf.core.elasticsearch;

import com.aliyun.opensearch.sdk.dependencies.com.google.gson.Gson;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.network.InetAddresses;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.transport.TransportAddress;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.completion.CompletionSuggestionBuilder;
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilder;
import org.elasticsearch.transport.client.PreBuiltTransportClient;

import static org.elasticsearch.common.xcontent.XContentFactory.*;

import java.util.*;

public class EsSearch {
    private static TransportClient transportClient = null;

    static {
        init();
    }

    /**
     * 初始化elasticsearch
     */
    public static void init(){
        Settings settings = Settings.builder()
                .put("cluster.name", "my-application")
                .put("client.transport.sniff", true)
                .build();
        transportClient = new PreBuiltTransportClient(settings);
        TransportAddress transportAddress =  new TransportAddress(InetAddresses.forString("10.230.2.140"), 9300);
        transportClient.addTransportAddresses(transportAddress);
    }

    public static void createIndex(String index,String type){
        try {
           /* PUT /my_index
            {
                "settings": {
                "analysis": {
                    "char_filter": {
                        "&_to_and": {
                            "type":       "mapping",  //转换器
                             "mappings": [ "&=> and "]
                        }},
                    "filter": {
                        "my_stopwords": {
                            "type":       "stop",  //停词器
                             "stopwords": [ "the", "a" ]
                        }},
                    "analyzer": {
                        "my_analyzer": {     //自定义分析器名
                            "type":         "custom",
                            "char_filter":  [ "html_strip", "&_to_and" ],
                            "tokenizer":    "standard",
                            "filter":       [ "lowercase", "my_stopwords" ] //词元过滤器
                        }}
                }}}
                //创建索引别名
                PUT /my_index_v1
                PUT /my_index_v1/_alias/my_index
                //更改别名操作原子化
                POST /_aliases
                {
                    "actions": [
                        { "remove": { "index": "my_index_v1", "alias": "my_index" }},
                        { "add":    { "index": "my_index_v2", "alias": "my_index" }}
                    ]
                }
                */
                try {
                    //索引设置
                    XContentBuilder settingsBuilder = XContentFactory.jsonBuilder()
                            .startObject()
                            .field("number_of_shards",5)//主分片数
                            .field("number_of_replicas",1)//副本数
                            .startObject("analysis")//定义新的分析器
                            .startObject("analyzer")
                            .startObject("DoubleAnalyzer")//分析器名称 GET /spanish_docs/_analyze?analyzer=DoubleAnalyzer   stopwords停用词
                            .field("tokenizer", "my_ngram_tokenizer")
                            .endObject()
                            .endObject()
                            .startObject("tokenizer")
                            .startObject("my_ngram_tokenizer")
                            .field("type", "ngram")
                            .field("min_gram", 2)
                            .field("max_gram", 2)
                            .field("token_chars", "letter, digit")
                            .endObject()
                            .endObject()
                            .endObject()
                            .endObject();
                    //索引结构
                    XContentBuilder builder = XContentFactory.jsonBuilder()
                            .startObject()
                            //date_detection=true|false 动态属性是否自动识别日期类型
                            .field("dynamic","true|false|strict")//动态映射 动态添加新的字段--缺省、忽略新的字段、如果遇到新字段抛出异常
                            .field("properties")
                            .startObject()
                            .field("title")
                            .startObject()
                            //对于 analyzed 字符串域，用 analyzer 属性指定在搜索和索引时使用的分析器。
                            // 默认， Elasticsearch 使用 standard 分析器， 但你可以指定一个内置的分析器替代它，
                            // 例如 whitespace 、 simple 和 `english`\ik_max_word：
                            .field("analyzer", "DoubleAnalyzer")
                            //全文 analyzed 字段排序会消耗大量的内存,所以用多字段映射，主字段用于全文搜索，子字段用作排序
                            .startObject("fields").startObject("field1").field("type","").field("type","not_analyzed").endObject().endObject()
                            .field("format","strict_date_optional_time||epoch_millis")//格式
                            /*字符串: text
                                整数 : byte, short, integer, long
                                浮点数: float, double
                                布尔型: boolean
                                日期: date
                                对象：object 子记录关系不存在
                                嵌套：nested  子记录关系存在
                                位置：geo_point 并行属性lat_lon:true控制经纬度分别索引,geohash_prefix:true标识geohashs映射，geohash_precision：1km精度
                                建议分析：completion*/
                            .field("type", "test")
                            //控制制怎样索引字符串,只有type为test有效。 域 index 属性默认是 analyzed 。
                            // 如果我们想映射这个字段为一个精确值，我们需要设置它为 not_analyzed ：
                            //其他简单类型（例如 long ， double ， date 等）也接受 index 参数，
                            // 但有意义的值只有 no 和 not_analyzed ， 因为它们永远不会被分析。
                            .field("index", "analyzed|not_analyzed|no")
                            .field("fielddata",true)//如果字符类型字段要分组，必须加此设置
                            .endObject()
                            .field("content")
                            .startObject()
                            .field("analyzer", "DoubleAnalyzer")
                            .field("type", "string")
                            .endObject()
                            .endObject()
                            .endObject();

                    CreateIndexResponse indexResponse = transportClient
                            .admin()
                            .indices()
                            .prepareCreate(index)
                            .setSettings(settingsBuilder).addMapping(type,builder)
                            .get();

                    System.out.println(indexResponse.isAcknowledged()); // true表示创建成功
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
        } catch (ElasticsearchException e) {
            e.printStackTrace();
        }
    }

    /**.
     *
     * 构建索引及文档
     * @param index 索引名称
     * @param type 索引类型
     * @param id
     * @fields 数据模型
     */
    public static void push(String index,String type,String id,Map<String,Object> fields) {
        if(fields.isEmpty()){
            return ;
        }
        try {
            XContentBuilder builder = jsonBuilder().startObject();
            for(Map.Entry<String, Object> entry : fields.entrySet()){
                builder.field(entry.getKey(),entry.getValue());
            }
            IndexResponse response = transportClient.prepareIndex(index, type, id).setSource(builder.endObject()).execute().actionGet();
        }catch (Exception e){
            e.printStackTrace();
        }
        //System.out.println(response.status()+" ----"+response.getIndex());
    }

    /**
     * 获取索引信息
     * @param index
     * @param type
     * @param id
     * @throws Exception
     */
    public static void get(String index,String type,String id) throws Exception{
        GetResponse response = transportClient.prepareGet(index,type,id).get();
        System.out.println(response.getIndex());
    }

    /**
     * 删除文档
     * @param index
     * @param type
     * @param id
     */
    public static void delete(String index,String type,String id){
        DeleteResponse response = transportClient.prepareDelete(index,type,id).get();
        System.out.println(response.status());
    }

    /**
     * 根据给定的查询条件删除一组文档
     * @param name 条件名称
     * @param text 条件值
     * @param source 索引-可以多个索引
     */
    public static void delete(String name,String text, String... source){
        BulkByScrollResponse response = DeleteByQueryAction.INSTANCE.newRequestBuilder(transportClient)
                .filter(QueryBuilders.matchQuery(name,text)).source(source).get();
        long deleted = response.getDeleted();
        System.out.println(deleted);
    }

    /**
     * 根据给定的查询条件删除一组文档（查询条件根据短句匹配）
     * @param name 条件名称
     * @param text 条件值
     * @param source 索引-可以多个索引
     */
    public static void deleteQueryShortSentenceMatch(String name,String text, String... source){
        BulkByScrollResponse response = DeleteByQueryAction.INSTANCE.newRequestBuilder(transportClient)
                .filter(QueryBuilders.matchPhraseQuery(name,text)).source(source).get();
        long deleted = response.getDeleted(); // 删除的文档数
        System.out.println(deleted);
    }

    /**
     * 根据给定的查询条件删除一组文档 (提供大量数据异步删除，长时间运行并提供监听器。)
     * @param name 条件名称
     * @param text 条件值
     * @param source 索引-可以多个索引
     */
    public static void deleteMassData(String name,String text, String... source){
        DeleteByQueryAction.INSTANCE.newRequestBuilder(transportClient)
                .filter(QueryBuilders.matchQuery(name, text))
                .source(source)
                .execute(new ActionListener<BulkByScrollResponse>() {
                    @Override
                    public void onResponse(BulkByScrollResponse response) {
                        long deleted = response.getDeleted();
                        System.out.println(deleted);
                    }
                    @Override
                    public void onFailure(Exception e) {

                    }
                });
    }

    /**
     * 更新一条数据
     * @param index 索引
     * @param type 索引类型
     * @param id
     * @param fieldMap 更新的字段
     * @throws Exception
     */
    public static void update(String index,String type,String id,Map<String,Object> fieldMap) throws  Exception{
        XContentBuilder builder = jsonBuilder().startObject();
        for(Map.Entry<String, Object> entry : fieldMap.entrySet()){
            builder.field(entry.getKey(),entry.getValue());
        }
        transportClient.prepareUpdate(index,type,id).setDoc(builder.endObject()).get();
    }

    /**
     * 按索引和查询值搜索数据
     * @param index 索引名
     * @param value 查询值
     * @return
     */
    public static String suggest(String index,String value){
        SearchRequestBuilder builder = transportClient.prepareSearch(index);
        builder.setQuery(QueryBuilders.matchAllQuery());
        CompletionSuggestionBuilder completionSuggestionBuilder = new
                CompletionSuggestionBuilder("nameAdd.suggest");
        completionSuggestionBuilder.prefix(value);
        PhraseSuggestionBuilder termSuggestionBuilder = new PhraseSuggestionBuilder("nameAdd.suggest");
        termSuggestionBuilder.text(value);
        //termSuggestionBuilder.suggestMode(TermSuggestionBuilder.SuggestMode.POPULAR);
        termSuggestionBuilder.size(7);
        //completionSuggestionBuilder.text(value);
        completionSuggestionBuilder.size(7);
        SuggestBuilder sb = new SuggestBuilder();
        sb.addSuggestion("my-suggest",completionSuggestionBuilder);
        builder.suggest(sb);
        //聚合
        /*AggregationBuilder teamAgg= AggregationBuilders.terms("aggs_nameAdd ").field("nameAdd")
                .subAggregation(AggregationBuilders.topHits("top").size(1));
        builder.addAggregation(teamAgg);
        builder.addSort(SortBuilders.fieldSort("").order(SortOrder.ASC));*/

        /*AggregationBuilder aggregation =
                AggregationBuilders
                        .filters("agg", //分组过滤统计
                                new FiltersAggregator.KeyedFilter("men", QueryBuilders.termQuery("gender", "male")),//men类
                                new FiltersAggregator.KeyedFilter("women", QueryBuilders.termQuery("gender", "female")));//women类*/
        SearchResponse response = builder.get();

        List<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>  list =
                response.getSuggest().getSuggestion("my-suggest").getEntries();
        List<String> suggestList = new ArrayList<String>();
        if (list == null) {

        } else {
            for (Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option> e : list) {
                for (Suggest.Suggestion.Entry.Option option : e) {
                    suggestList.add(option.getText().toString());
                }
            }
        }
        System.out.println(suggestList.size());
        Gson gson = new Gson();
        return gson.toJson(suggestList);

    }

    public static String query(Integer pageNo,Integer pageSize,String index,String name,String value,String ...types) {

        SearchRequestBuilder builder = transportClient.prepareSearch(index)
                .setTypes(types)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH).setFrom(pageNo).setSize(pageSize).setExplain(true);
        //String str = PatternTest.checkChinese(value);
        //String[] s = str.split(";");


        //builder.setSource()
        if(!StringUtils.isEmpty(name) && !StringUtils.isEmpty(value)){
            QueryStringQueryBuilder queryBuilder = new QueryStringQueryBuilder(value);
            //queryBuilder.analyzer("ik_max_word");
            queryBuilder.field(name);
            //XContentBuilder mapping = null;
            builder.setQuery(QueryBuilders.matchQuery(name,value));//全文搜索
            builder.setQuery(QueryBuilders.multiMatchQuery(value,name,"pic"));//多个字段全文搜索
            builder.setQuery(QueryBuilders.termQuery(name,value));//精确值 匹配
            builder.setQuery(QueryBuilders.termsQuery(name,value,value));//多个精确值 匹配
            builder.setQuery(QueryBuilders.rangeQuery(name).gt(value).lt(value));//范围查询
            builder.setQuery(QueryBuilders.existsQuery(name));//有值查询
            //builder.setQuery(QueryBuilders.geoBoundingBoxQuery().);//范围查询
            //builder.setQuery(QueryBuilders.constantScoreQuery());//不评分
            //builder.setQuery(QueryBuilders.matchQuery(name,value));
            //for(int i = 0;i < s.length; i++ ){
           QueryBuilders.boolQuery().must(
                    QueryBuilders.multiMatchQuery("name","title","title_1"))
                    .must(QueryBuilders.multiMatchQuery( "title2","title3"))
           .must(QueryBuilders.nestedQuery("path", null, ScoreMode.Avg));//query:QueryBuilder同上嵌套使用
            //ScoreMode根文档的分数是这些嵌套文档分数的平均值。可以通过设置 score_mode 参数来控制这个得分策略，相关策略有 avg (平均值), max (最大值), sum (加和) 和 none (直接返回 1.0 常数值分数)));
            //    builder.setQuery(QueryBuilders.matchQuery(name,value));
            //}
            HighlightBuilder hiBuilder=new HighlightBuilder();
            hiBuilder.preTags("<font color='red'>");
            hiBuilder.postTags("</font>");
            hiBuilder.field(name);
            builder.highlighter(hiBuilder);
        }

        /*builder.addSort(new ScoreSortBuilder().order(SortOrder.DESC));
        FieldSortBuilder fieldSortBuilder = new FieldSortBuilder("_uid").order(SortOrder.ASC);
        fieldSortBuilder.sortMode(SortMode.AVG);
        fieldSortBuilder.setNestedFilter(QueryBuilders.rangeQuery("").gt(12));
        fieldSortBuilder.setNestedPath("");
        builder.addSort(fieldSortBuilder);*/
        SearchResponse response = builder.get();
                SearchHits hits = response.getHits();
        List list = new ArrayList();
        for (SearchHit searchHit : hits) {
            Map source = searchHit.getSourceAsMap();
            list.add(source);
            if(searchHit.getHighlightFields().size() > 0){
                Text[] texts = searchHit.getHighlightFields().get(name).getFragments();
                List<String> highlighList = new ArrayList<>();
                String str = "";
                for(Text text : texts){
                    str += text;
                }
                source.put("highlightField",str);
            }
        }
        Gson gson = new Gson();
        String json = gson.toJson(list);
        System.out.println(json);
        return json;
    }


    public static void main(String args[]) throws Exception{

        System.out.println(EsSearch.suggest("bmall2","奥"));
        //EsSearch.query(1,10,"bmall2","nameAdd","龙","feature");
        //Map map = new HashMap();
        //map.put("user","zhangsan");
        //map.put("message","message !!!");

        //EsSearch.update("index_test","type_test","1",map);

        String[] str = new String[]{"bmall"};
        //EsSearch.delete("message","ok",str);
        //EsSearch.deleteQueryShortSentenceMatch("user","zhangsan1",str);

        //EsSearch.deleteMassData("_type","feature",str);
        //Map map = new HashMap();
        //map.put("user","zhangsan1");
        //map.put("postDate",new Date());
        //map.put("message","trying out Elasticsearch ok");
        //for(int i = 0; i < 3; i++){
        //    EsSearch.push("index_test","type_test",Integer.valueOf(i).toString(),map);
        //}

        //EsSearch.get("index_test","type_test","1");
        //EsSearch.delete("index_test","type_test","2");
        //EsSearch.get("index_test","type_test","2");
        //
        //
        //transportClient.close();
        //
        ////transportClient.prepareUpdate("twitter1","tweet1","6").set
        //

    }
}
